/**
 * @file Define WeChat API methods
 * 
 * @author Steve Gao<gaofu@parllay.com>
 */
const request = require('request');
const async = require('async');
const qs = require('querystring');
const path = require('path');
const fs = require('fs');

const utils = require('../utils');

const {WeChatApiHelper, WeChatMessageHelper}        = require('../helpers/wechat-helpers');
const {END_POINTS, SNS_END_POINTS, MP_END_POINTS}   = require('../helpers/wechat-helpers');

/**
 * A wrapper class for WeChat API requests
 * 
 * @class WeChatAPI
 */
class WeChatAPI {
    /**
     * Create QRCode 
     * 
     * @name createQRCode
     * 
     * @description About how to create QRCode, 
     * please ref to {@link https://mp.weixin.qq.com/wiki/18/167e7d94df85d8389df6c94a7a8f78ba.html | QRCode}
     * 
     * @param {Object} params - The parameters
     * @param {String} accessToken - The access token to creating QRCode
     * 
     * @example
     * params = {
     *      is_persist: false,
     *      expire_seconds: 60,
     *      value : "dfadfadsfads"
     * }
     * 
     * @returns {void}
     */
    static createQRCode(accessToken, params, callback) {
        utils.checkStringParameter(accessToken, "accessToken");
        utils.checkObjectParameter(params, "params");

        var url = WeChatApiHelper.getWeChatServerEndPoint(END_POINTS.QR_CODE, {
            access_token: accessToken
        });
        
        var data = {
            "action_name" : (params.is_persist ? "QR_LIMIT_STR_SCENE" : "QR_SCENE"),
            "action_info" : {
                scene: {}
            }
        };

        if (!params.is_persist) {
            data.expire_seconds = params.expire_seconds || 86400 * 14;
        }

        if (!params.is_persist) {
            data.action_info.scene.scene_id = params.value;
        } else {
            data.action_info.scene.scene_str = params.value;
        }

        utils.curlPost(url, data, function(error, data) {
            callback(error, JSON.parse(data));
        });
    }

    /**
     * 
     * Get QRCode url
     * 
     * @name getQRCodeURL
     * @param {String} ticket - The QR Code ticket
     * 
     * @returns {String}
     * @memberOf WeChatAPI
     */
    static getQRCodeURL(ticket) {
        var url = WeChatApiHelper.getWeChatMPServerEndPoint(MP_END_POINTS.SHOW_QR_CODE, {
            ticket: ticket
        });

        return url;
    }

    /**
     * Get access token by appId and secret
     * 
     * @name getAccessToken
     * @param {Object} params - the parameters which to get access token
     * @param {Function} callback - callback function
     * 
     * @example 
     * WeChatAPI.getAccessToken({
     *    app_id: 'wxadfadfadsfasdfasdf',
     *    secret: 'dakf;dfja;ldfja;dfja;sdfa'
     * })
     * 
     * 
     * @returns {void}
     */
    static getAccessToken(params, callback) {
        callback = typeof callback === 'function' ? callback : function(data) {console.log(data);};
        var url = WeChatApiHelper.getWeChatServerEndPoint(END_POINTS.GET_TOKEN, params);
        request(url, function(error, response, body) {
            if(!error && response.statusCode == 200) {
                var data = JSON.parse(body);
                callback(data);
            } else {
                callback(null)
            }
        });
    }

    /**
     * Get API ticket
     * 
     * @name getApiTicket
     * @param {Object} params - the parameters which to get API ticket
     * @example 
     * WeChatAPI.getApiTicket({
     *  access_token: 'token',
     *  type: 'jsapi'
     * }, function(ticket) {
     *  console.log(ticket);
     * });
     * @param {Function} callback - callback function
     * 
     * @returns {void}
     */
    static getApiTicket(params, callback) {
        callback = typeof callback === 'function' ? callback : function(data) {console.log(data);};

        var url = WeChatApiHelper.getWeChatServerEndPoint(END_POINTS.GET_TICKET, params);
        request(url, function(error, response, body) {
            if(!error && response.statusCode == 200) {
                var data = JSON.parse(body);
                callback(data);
            } else {
                callback(null);
            }
        });
    }

    /**
     * Get platform access token 
     * 
     * @name getPlatformAccessToken
     * @param {Object} params - The parameters to get platform access token
     * @param {Function} callback - The callback function
     * 
     * @example
     * WeChatAPI.getPlatformAccessToken({
     *       'component_appid' : me.wechatConfig.component_app_id,
     *       'component_appsecret' : me.wechatConfig.component_app_secret,
     *       'component_verify_ticket' : verifyTicket
     * }, function(data) {
     *     console.log(data.component_access_token, verifyTicket);
     * });
     * @returns {void}
     */
    static getPlatformAccessToken(params, callback) {
        callback = typeof callback === 'function' ? callback : function(data) {console.log(data);};

        var url = WeChatApiHelper.getWeChatServerEndPoint(END_POINTS.COMPONENT_ACCESS_TOKEN);
        utils.sendRequest(url, 'POST', {
            'content-type' : 'application/json'
        }, params, (data) => {
            if(data) {
                data = JSON.parse(data);
                callback(data);
            } else {
                callback(null);
            }
        });
    }

    /**
     * Get platform pre auth code
     * 
     * @name getPlatformPreAuthCode
     * @param {Object} params - The parameters to get pre_auth_code
     * @param {Function} callback - The callback function
     * 
     * @example
     * WeChatAPI.getPlatformPreAuthCode({
     *       component_app_id: componentAppId,
     *       token: componentAccessToken
     * }, function(data) {
     *     if(data && data.pre_auth_code) {
     *         parentCallback(data.pre_auth_code);
     *     } else {
     *         parentCallback('FAILED');
     *     }
     * });
     * 
     * @returns {void}
     */
    static getPlatformPreAuthCode(params, callback) {
        callback = typeof callback === 'function' ? callback : function(data) {console.log(data);};
        var url = WeChatApiHelper.getWeChatServerEndPoint(END_POINTS.PRE_AUTH_CODE, {
            component_access_token: params.token
        });

        utils.curlPost(url, {
            'component_appid' : params.component_app_id
        }, function(error, data) {
            callback(error, JSON.parse(data));
        });
    }

    /**
     * Get WeChat User OAuth URL
     * 
     * @name getWeChatUserOAuthUrl
     * @param {String} appId - The WeChat Offical Account AppId 
     * @param {String} redirectUri - The redirect url after OAuth 
     * @param {String} state - The state code
     * @param {String} componentAppId - The WeChat third party application id
     * 
     * @returns {String}
     */
    static getWeChatUserOAuthUrl(appId, redirectUri, state, componentAppId) {
        var params = {};

        params.appid = appId;
        params.redirect_uri = redirectUri;
        params.response_type = 'code';
        params.scope = 'snsapi_base';
        
        if(state) {
            params.state = state;
        }
        if(componentAppId) {
            params.component_appid = componentAppId;
        }

        return WeChatApiHelper.getWeChatSNSServerEndPoint(SNS_END_POINTS.USER_OAUTH_URL, params) + '#wechat_redirect';
    }

    /**
     * Get platform OAuth URL
     * 
     * @name getPlatformOAuthURL
     * @param {String} appId - The WeChat Official Account AppId 
     * @param {String} preAuthCode - The pre_auth_code from the third party platform
     * @param {String} redirectUri - The redirect uri after OAuth
     * 
     * @returns {String}
     */
    static getPlatformOAuthURL(appId, preAuthCode, redirectUri) {
        var params = {};
        params.component_appid = appId;
        params.pre_auth_code = preAuthCode;
        params.redirect_uri = redirectUri;

        var url = WeChatApiHelper.getWeChatMPServerEndPoint(MP_END_POINTS.MP_OAUTH_URL, params);
        return url;
    }
    
    /**
     * Get authorizer access token 
     * 
     * @name getAuthorizerAccessToken
     * @param {String} componentAppId - The WeChat third-party application id
     * @param {String} componentToken - The WeChat third-party application access token
     * @param {String} authorizationCode - The authorization code from WeChat third-party platform
     * @param {Function} callback - The callback function
     * 
     * @returns {void}
     */
    static getAuthorizerAccessToken(componentAppId, componentToken, authorizationCode, callback) {
        var url = WeChatApiHelper.getWeChatServerEndPoint(END_POINTS.GET_AUTHORIZER_TOKEN, {
            component_access_token: componentToken
        });
        utils.curlPost(url, {
            component_appid: componentAppId,
            authorization_code: authorizationCode
        }, function(error, data) {
            callback(error, JSON.parse(data));
        });
    }

    /**
     * Refresh authorizer's access token 
     * 
     * @name refreshAuthorizerAccessToken
     * @param {String} componentAppId - The WeChat third-party application id
     * @param {String} componentAccessToken - The WeChat third-party application access token
     * @param {String} authorizerAppId - The WeChat Official Account AppId 
     * @param {String} refreshToken - The access token to refresh the token of authorizer 
     * @param {Function} callback - The callback function 
     * 
     * @returns {void}
     */
    static refreshAuthorizerAccessToken(componentAppId, componentToken, authorizerAppId, refreshToken, callback) {

        var url = WeChatApiHelper.getWeChatServerEndPoint(END_POINTS.REFRESH_AUTHORIZER_TOKEN, {
            component_access_token: componentToken
        });
        
        utils.curlPost(url, {
            component_appid: componentAppId,
            authorizer_appid: authorizerAppId,
            authorizer_refresh_token: refreshToken
        }, (error, data) => {
            try {
                callback(error, JSON.parse(data));
            } catch(e) {
                callback(error, null);
            }
        });
    }

    /**
     * Get Offical Account's basic information
     * 
     * @name getOfficialAccountBaseInfo
     * @param {String} componentAppId - The WeChat third-party's application id
     * @param {String} componentToken - The WeChat third-party's access token
     * @param {String} authorizerAppId - The Official Account's AppId
     * 
     * @returns {void}
     */
    static getOfficialAccountBaseInfo(componentAppId, componentToken, authorizerAppId, callback) {
        var url = WeChatApiHelper.getWeChatServerEndPoint(END_POINTS.GET_AUTHORIZER_INFO, {
            component_access_token: componentToken
        });
        utils.curlPost(url, {
            component_appid : componentAppId, 
            authorizer_appid: authorizerAppId
        }, function(error, data) {
            if(data) {
                callback(JSON.parse(data));
            } else {
                callback(null);
            }
        });
    }

    /**
     * Get the access token of fan for offical account
     * 
     * @name getWeChatUserAccessToken
     * @param {String} appId - The Offical Account's AppId
     * @param {String} secretOrToken - The secret of Official Account or the access token of Offical Account
     * @param {String} code - The code to getting access token
     * @param {String} componentAppId - The WeChat third-party application id
     * @param {Function} callback - The callback function
     * 
     * @returns {void}
     */
    static getWeChatUserAccessToken(appId, secretOrToken, code, componentAppId, callback) {
        callback = typeof callback === 'function' ? callback : function(data) {console.log(data);};
        var params = {
            appid: appId,
            code: code,
            grant_type: 'authorization_code'
        };
        if(componentAppId) {
            params.component_appid = componentAppId;
            params.component_access_token = secretOrToken;
        } else {
            params.secret = secretOrToken;
        }

        var url = WeChatApiHelper.getWeChatServerEndPoint((componentAppId ? END_POINTS.COMPONENT_USER_OAUTH_TOKEN : END_POINTS.USER_OAUTH_TOKEN), params);
        request(url, function(error, response, body) {
            if(!error && response.statusCode == 200) {
                var data = JSON.parse(body);
                callback(data);
            } else {
                callback(null)
            }
        });
    }

    /**
     * Get fan's information of the Official Account
     * 
     * @name getWeChatUserInfo
     * @param {String} token - The fan's access token
     * @param {String} appId - The fan's openId
     * @param {String} type - The request type, the possible value is oauth or normal
     * @param {Function} callback - The callback function
     * 
     * @returns {void}
     */
    static getWeChatUserInfo(token, openId, type, callback) {
        callback = typeof callback === 'function' ? callback : function(data) {console.log(data);};
        var params = {
            openid: openId,
            access_token: token,
            lang: 'zh_CN'
        }
        var url = WeChatApiHelper.getWeChatServerEndPoint((type === 'oauth' ? END_POINTS.USER_INFO : END_POINTS.SNS_USER_INFO), params);
        request(url, function(error, response, body) {
            if(!error && response.statusCode == 200) {
                var data = JSON.parse(body);
                callback(data);
            } else {
                callback(null);
            }
        });
    }

    /**
     * Download the media from WeChat's server
     * 
     * @name downloadMediaById
     * @param {String} token - The Official Account's access token
     * @param {String} mediaId - The WeChat's media id
     * @param {Function} callback - The callback function
     * 
     * @returns {void}
     */
    static downloadMediaById(token, mediaId, callback) {
        callback = typeof callback === 'function' ? callback : function(data) {console.log(data);};
        var params = {
            access_token: token,
            media_id: mediaId
        };
        
        var url = WeChatApiHelper.getWeChatServerEndPoint(END_POINTS.GET_MEDIA, params);

        var req = request.get(url);
        req.on('response', function(response) {
            var contentType = response.headers['content-type'];
            var items = contentType.split('/');
            var extension = items[1];
            var date = new Date();
            var year = date.getFullYear();
            var month = date.getMonth() + 1;
            var day = date.getDate();
            var hour = date.getHours();
            var pathKey = '' + date.getFullYear() + (month < 10 ? '0' + month : month) + (day < 10 ? '0' + day : day) + (hour < 10 ? '0' + hour : hour);
            
            var fileKey = utils.uuid() + '.' + extension;
            var subpath = '/static/uploads/' + pathKey
            var filepath = path.join(process.cwd(), subpath);
            if(!fs.existsSync(filepath)) {
                fs.mkdirSync(filepath);
            }
            
            var realFile = path.join(filepath, fileKey);
            if(items[0].toLowerCase() !== 'image') {
                callback({
                    error: '下载暂时不可用'
                });
            } else {
                req.pipe(fs.createWriteStream(realFile)).on('close', function() {
                    callback({
                        realFile: realFile,
                        fileKey: path.join(pathKey, fileKey)
                    });
                });
            }
        });
        
    }
    
    /**
     * Send customer service message 
     * 
     * @name sendCustomerServiceMessage
     * @param {String} accessToken - The official account's access token
     * @param {Object} message - The customer service object 
     * @param {Function} callback - The callback function
     * 
     * @returns {void} 
     */
    static sendCustomerServiceMessage(accessToken, message, callback) {

        var csEndPoint = WeChatApiHelper.getWeChatServerEndPoint(END_POINTS.SEND_CUSTOMER_SERVICE_MESSAGE, {
            access_token: accessToken
        });


        utils.curlPost(csEndPoint, message, function(error, data) {
            callback(error, data);
        });
    }
}

exports.WeChatAPI = WeChatAPI;
